#include "stdafx.h"
#include "Base64Coding.h"
#pragma hdrstop

static const char          fillchar = '=';
static const string::size_type np = string::npos;

const string CBase64Coding::Base64Table(
								 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");

// Decode Table gives the index of any valid base64 character in the Base64 table]
// 65 == A, 97 == a, 48 == 0, 43 == +, 47 == /

// 0  1  2  3  4  5  6  7  8  9
const string::size_type CBase64Coding::DecodeTable[] = {
	np,np,np,np,np,np,np,np,np,np,  // 0 - 9
	np,np,np,np,np,np,np,np,np,np,  //10 -19
	np,np,np,np,np,np,np,np,np,np,  //20 -29
	np,np,np,np,np,np,np,np,np,np,  //30 -39
	np,np,np,62,np,np,np,63,52,53,  //40 -49
	54,55,56,57,58,59,60,61,np,np,  //50 -59
	np,np,np,np,np, 0, 1, 2, 3, 4,  //60 -69
	5, 6, 7, 8, 9,10,11,12,13,14,  //70 -79
	15,16,17,18,19,20,21,22,23,24,  //80 -89
	25,np,np,np,np,np,np,26,27,28,  //90 -99
	29,30,31,32,33,34,35,36,37,38,  //100 -109
	39,40,41,42,43,44,45,46,47,48,  //110 -119
	49,50,51,np,np,np,np,np,np,np,  //120 -129
	np,np,np,np,np,np,np,np,np,np,  //130 -139
	np,np,np,np,np,np,np,np,np,np,  //140 -149
	np,np,np,np,np,np,np,np,np,np,  //150 -159
	np,np,np,np,np,np,np,np,np,np,  //160 -169
	np,np,np,np,np,np,np,np,np,np,  //170 -179
	np,np,np,np,np,np,np,np,np,np,  //180 -189
	np,np,np,np,np,np,np,np,np,np,  //190 -199
	np,np,np,np,np,np,np,np,np,np,  //200 -209
	np,np,np,np,np,np,np,np,np,np,  //210 -219
	np,np,np,np,np,np,np,np,np,np,  //220 -229
	np,np,np,np,np,np,np,np,np,np,  //230 -239
	np,np,np,np,np,np,np,np,np,np,  //240 -249
	np,np,np,np,np,np               //250 -256
};


CBase64Coding::CBase64Coding()
{
}

CBase64Coding::~CBase64Coding()
{
}

string CBase64Coding::Encode(const string & data)
{
	string::size_type  i;
	char               c;
	string::size_type  len = data.length();
	string             ret;

	ret.reserve(len * 2);

	for (i = 0; i < (size_t)len; ++i)
	{
		c = (data[i] >> 2) & 0x3f;
		ret.append(1, Base64Table[c]);
		c = (data[i] << 4) & 0x3f;
		if (++i < len)
			c |= (data[i] >> 4) & 0x0f;

		ret.append(1, Base64Table[c]);
		if (i < len)
		{
			c = (data[i] << 2) & 0x3f;
			if (++i < len)
				c |= (data[i] >> 6) & 0x03;

			ret.append(1, Base64Table[c]);
		}
		else
		{
			++i;
			ret.append(1, fillchar);
		}

		if (i < len)
		{
			c = data[i] & 0x3f;
			ret.append(1, Base64Table[c]);
		}
		else
		{
			ret.append(1, fillchar);
		}
	}

	return(ret);
}


string CBase64Coding::Encode( const char * source, int len)
{
	size_t             i;
	char               c;
	string             ret;

	ret.reserve(len * 2);

	for (i = 0; i < (size_t)len; ++i)
	{
		c = (source[i] >> 2) & 0x3f;
		ret.append(1, Base64Table[c]);
		c = (source[i] << 4) & 0x3f;
		if (++i < len)
			c |= (source[i] >> 4) & 0x0f;

		ret.append(1, Base64Table[c]);
		if (i < len)
		{
			c = (source[i] << 2) & 0x3f;
			if (++i < len)
				c |= (source[i] >> 6) & 0x03;

			ret.append(1, Base64Table[c]);
		}
		else
		{
			++i;
			ret.append(1, fillchar);
		}

		if (i < len)
		{
			c = source[i] & 0x3f;
			ret.append(1, Base64Table[c]);
		}
		else
		{
			ret.append(1, fillchar);
		}
	}

	return(ret);
}

string
CBase64Coding::Decode(const string& data)
{
	string::size_type  i;
	char               c;
	char               c1;
	string::size_type  len = data.length();
	string             ret;

	ret.reserve(len);

	for (i = 0; i < (size_t)len; ++i)
	{
		c = static_cast<char>(DecodeTable[static_cast<unsigned char>(data[i])]);
		++i;
		c1 = static_cast<char>(DecodeTable[static_cast<unsigned char>(data[i])]);
		c = (c << 2) | ((c1 >> 4) & 0x3);
		ret.append(1, c);
		if (++i < len)
		{
			c = data[i];
			if (fillchar == c)
				break;

			c = static_cast<char>(DecodeTable[static_cast<unsigned char>(data[i])]);
			c1 = ((c1 << 4) & 0xf0) | ((c >> 2) & 0xf);
			ret.append(1, c1);
		}

		if (++i < len)
		{
			c1 = data[i];
			if (fillchar == c1)
				break;

			c1 = static_cast<char>(DecodeTable[static_cast<unsigned char>(data[i])]);
			c = ((c << 6) & 0xc0) | c1;
			ret.append(1, c);
		}
	}

	return(ret);
}